﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Threading;

namespace LMS.DocumentConvertService
{
    public class ServiceListener : IDisposable
    {
        private readonly HttpListener _listener;
        private readonly Thread _listenerThread;
        private readonly ManualResetEvent _stop, _idle;
        private readonly Semaphore _busy;

        public ServiceListener()
        {
            _stop = new ManualResetEvent(false);
            _idle = new ManualResetEvent(false);
            _busy = new Semaphore(Program.MaxThreads, Program.MaxThreads);
            _listener = new HttpListener();
            _listenerThread = new Thread(HandleRequests);
        }

        public void Start()
        {
            var url = String.Format(@"http://localhost:{0}/", Program.ListenerPort);
            LogHelper.Info<ServiceListener>("Listenning Start:" + url);
            _listener.Prefixes.Add(url);
            _listener.Start();
            _listenerThread.Start();
        }

        public void Stop()
        {
            _stop.Set();
            _listenerThread.Join();
            _idle.Reset();

            //aquire and release the semaphore to see if anyone is running, wait for idle if they are.
            _busy.WaitOne();
            if (Program.MaxThreads != 1 + _busy.Release())
                _idle.WaitOne();

            _listener.Stop();

            LogHelper.Info<ServiceListener>("Listenning stoped:");
        }

        private void HandleRequests()
        {
            while (_listener.IsListening)
            {
                var context = _listener.BeginGetContext(ListenerCallback, null);

                if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
                {
                    return;
                }
            }
        }

        private void ListenerCallback(IAsyncResult ar)
        {
            _busy.WaitOne();
            try
            {
                HttpListenerContext context;
                try
                {
                    context = _listener.EndGetContext(ar);
                }
                catch (HttpListenerException ex)
                {
                    Console.Write(ex.ToString());
                    return;
                }

                if (_stop.WaitOne(0, false))
                    return;

                DateTime start = DateTime.Now;
                string url = context.Request.RawUrl;
                string httpMethod = context.Request.HttpMethod;
                string contentType = context.Request.ContentType;
                NameValueCollection queryString = context.Request.QueryString;

                if (context.Request.Url.LocalPath == "/clean")
                {
                    #region 执行清理
                    ConvertHelper.CleanProccess();


                    var response = context.Response;
                    context.Response.SendChunked = true;
                    string responseString = Newtonsoft.Json.JsonConvert.SerializeObject(new { success = true });
                    byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);

                    //对客户端输出相应信息.
                    response.ContentLength64 = buffer.Length;
                    response.ContentType = "application/json";
                    System.IO.Stream output = response.OutputStream;
                    output.Write(buffer, 0, buffer.Length);
                    output.Close();

                    #endregion
                }
                else if (context.Request.Url.LocalPath == "/")
                {
                    #region 执行转换
                    //string formDataString = null;
                    //if (context.Request.HasEntityBody)
                    //{
                    //    using (Stream body = context.Request.InputStream)
                    //    {
                    //        using (System.IO.StreamReader reader = new StreamReader(body, context.Request.ContentEncoding))
                    //        {
                    //            formDataString = reader.ReadToEnd();
                    //        }
                    //    }
                    //}

                    //NameValueCollection formData = System.Web.HttpUtility.ParseQueryString(formDataString);

                    string sourcePath = queryString["srcPath"];
                    string targetpath = queryString["tarPath"];
                    string errMessage = string.Empty;
                    try
                    {
                        ConvertHelper.ConvetToPdf(sourcePath, targetpath);
                    }
                    catch (Exception ex)
                    {
                        errMessage = ex.Message;
                        LogHelper.Error<ConvertHelper>(ex, "转换文件 {0} Exception", sourcePath);
                    }

                    ResultJson json = new ResultJson
                    {
                        CostSeconds = Convert.ToInt32(DateTime.Now.Subtract(start).TotalSeconds),
                        Message = string.IsNullOrEmpty(errMessage) ? (File.Exists(targetpath) ? "OK" : "Error") : errMessage,
                        Success = File.Exists(targetpath),
                        SourcePath = sourcePath,
                        TargetPath = targetpath
                    };

                    var response = context.Response;
                    context.Response.SendChunked = true;

                    string responseString = Newtonsoft.Json.JsonConvert.SerializeObject(json);
                    byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);

                    //对客户端输出相应信息.
                    response.ContentLength64 = buffer.Length;
                    response.ContentType = "application/json";
                    context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                    context.Response.OutputStream.Close();

                    LogHelper.Info<ServiceListener>("{0:yyyy-MM-dd HH:mm:ss:fff} {1} -> {2} Take {3}ms", DateTime.Now, sourcePath, targetpath, (DateTime.Now - start).TotalMilliseconds);
                    #endregion
                }
                else
                {
                    byte[] body = System.Text.Encoding.UTF8.GetBytes("Not found");
                    context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    context.Response.ContentType = "text/plain";
                    context.Response.OutputStream.Write(body, 0, body.Length);
                    context.Response.OutputStream.Close();
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error<ConvertHelper>(ex, "Unknown Exception");
            }
            finally
            {
                if (Program.MaxThreads == 1 + _busy.Release())
                    _idle.Set();
            }
        }

        public void Dispose()
        {
            Stop();
        }
    }

    internal class ResultJson
    {
        public string SourcePath { get; set; }

        public string TargetPath { get; set; }

        public bool Success { get; set; }

        public int CostSeconds { get; set; }

        public string Message { get; internal set; }
    }
}